//
// Created by QU on 24-9-18.
//
// 完全重写的一个msgNode, 基类是basicNode, 派生出sendNode和recvNode.
// 在MsgNode中存放了他们的头分离版本.
#pragma once

#ifndef RECVNODE_SENDNODE_H
#define RECVNODE_SENDNODE_H

#include <iostream>
#include <algorithm>
#include <ranges>
#include <memory>
#include <limits>

class basicNode
{
public:
    constexpr size_t MAX_PAYLOAD_SIZE = 1024;

    struct CStrPayload
    {
        int32_t length;
        std::unique_ptr<char8_t[]> payload;
    };


    explicit basicNode(const int32_t payload_len)
        : m_curr_len(0), m_payload_len(payload_len), m_payload(std::make_unique<char8_t[]>(payload_len))
    {
        check_payload_len(payload_len);
        // Initialize payload to zero
    }

    explicit basicNode(std::unique_ptr<char8_t[]> payload, const int32_t payload_len)
        : m_curr_len(0), m_payload_len(payload_len), m_payload(std::move(payload))
    {
        check_payload_len(payload_len);
    }

    explicit basicNode(CStrPayload payload)
        : m_curr_len(0), m_payload_len(payload.length), m_payload(std::move(payload.payload))
    {
        check_payload_len(payload.length);
    }

    friend std::ostream& operator<<(std::ostream& os, const basicNode& node)
    {
        os << "Payload Length: " << node.m_payload_len << "\n";
        os << "Current Length: " << node.m_curr_len << "\n";
        os << "Payload: ";
        for (size_t i = 0; i < node.m_payload_len; ++i)
        {
            os << static_cast<char>(node.m_payload[i]); // Convert char8_t to char
        }
        os << "\n";
        return os;
    }

    [[nodiscard]] virtual size_t getNodeByteLength() const
    {
        return m_payload_len + sizeof(m_payload_len);
    }

    [[nodiscard]] static CStrPayload createCStrPayload(const char8_t* cmessage)
    {
        size_t clen = std::char_traits<char8_t>::length(cmessage);
        int32_t length = (clen <= std::numeric_limits<int32_t>::max()
                              ? static_cast<int32_t>(clen)
                              : std::numeric_limits<int32_t>::max());
        std::cout << "createCStrPayload length: " << length << "\n";

        auto payload = std::make_unique<char8_t[]>(length);
        std::ranges::copy(std::span(cmessage, length), payload.get());
        // std::span 是 C++20 引入的一个标准库类型，它提供了对连续内存块的视图，而不拥有这些内存块。
        // std::span 类似于指针和长度的组合，但它提供了更安全和更方便的接口。主要用于在不复制数据的情况下传递数组或容器的子范围。

        return { length, std::move(payload) }; // 返回自定义结构体
    }

    virtual ~basicNode() = default;

    // Method to set payload data
    void setPayload(std::unique_ptr<char8_t[]> payload, const int32_t length)
    {
        m_payload = std::move(payload);
        m_payload_len = length;
        m_curr_len = length;
    }

    void setPayload(CStrPayload payload)
    {
        m_payload = std::move(payload.payload);
        m_payload_len = payload.length;
        m_curr_len = 0;
    }

    // Method to get the current length of the payload
    [[nodiscard]] int32_t getCurrentLength() const
    {
        return m_curr_len;
    }

    // Method to get the maximum payload length
    [[nodiscard]] int32_t getPayloadLength() const
    {
        return m_payload_len;
    }

protected:
    int32_t m_curr_len { 0 }; /* not set value */
    int32_t m_payload_len { 0 };
    std::unique_ptr<char8_t[]> m_payload;

private:
    static void check_payload_len(int32_t payload_len)
    {
        if (payload_len > MAX_PAYLOAD_SIZE)
        {
            throw std::invalid_argument("payload length too large");
        }
    }
};

class sendNode : public basicNode
{
public:
    explicit sendNode(const int32_t payload_len, const int8_t type_tag, const int8_t msg_id)
        : basicNode(payload_len), m_type_tag(type_tag), m_msg_id(msg_id)
    {
    }

    explicit sendNode(std::unique_ptr<char8_t[]> payload, const int32_t payload_len, const int8_t type_tag,
                      const int8_t msg_id)
        : basicNode(std::move(payload), payload_len), m_type_tag(type_tag), m_msg_id(msg_id)
    {
    }

    friend std::ostream& operator<<(std::ostream& os, const sendNode& node)
    {
        os << "Node Byte Length:" << node.getNodeByteLength() << "\n";
        os << "Type Tag: " << static_cast<char>(node.m_type_tag) << "\n";
        os << "Message ID: " << static_cast<char>(node.m_msg_id) << "\n";
        os << static_cast<const basicNode&>(node); // Call base class output
        return os;
    }

    [[nodiscard]] size_t getNodeByteLength() const override
    {
        return m_payload_len + sizeof(m_payload_len) + sizeof(m_type_tag) + sizeof(m_msg_id);
    }

private:
    int8_t m_type_tag;
    int8_t m_msg_id;
};

class recvNode : public basicNode
{
public:
    explicit recvNode(const int32_t payload_len, int8_t type_tag, int8_t msg_id)
        : basicNode(payload_len), m_type_tag(type_tag), m_msg_id(msg_id)
    {
    }

    explicit recvNode(std::unique_ptr<char8_t[]> payload, const int32_t payload_len, const int8_t type_tag,
                      const int8_t msg_id)
        : basicNode(std::move(payload), payload_len), m_type_tag(type_tag), m_msg_id(msg_id)
    {
    }

    friend std::ostream& operator<<(std::ostream& os, const recvNode& node)
    {
        os << "Node Byte Length:" << node.getNodeByteLength() << "\n";
        os << "Type Tag: " << static_cast<char>(node.m_type_tag) << "\n";
        os << "Message ID: " << static_cast<char>(node.m_msg_id) << "\n";
        os << static_cast<const basicNode&>(node); // Call base class output
        return os;
    }

    [[nodiscard]] size_t getNodeByteLength() const override
    {
        return m_payload_len + sizeof(m_payload_len) + sizeof(m_type_tag) + sizeof(m_msg_id);
    }

private:
    int8_t m_type_tag;
    int8_t m_msg_id;
};

 int main()
 {
     sendNode sn1(10, 'S', '1');
     std::cout << sn1;
     std::cout << "\n";

     constexpr char8_t msg[] = u8"Hello, world!";
     auto [length, payload] = sendNode::createCStrPayload(msg);
     sendNode sn2((std::move(payload)), length, 'S', '2');
     std::cout << sn2;
     std::cout << "\n";

     recvNode receiver(10, 'R', '2');
     std::cout << receiver;
     std::cout << "\n";
     return 0;
 }

#endif
